﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Snowdreamist.ComponentModel;
using Snowdreamist.Threading;

namespace Snowdreamist.Windows.Notification
{
    public class NotificationJob : CriticalDisposableObject
    {
        public NotificationScope Scope { get; set; }

        public NotificationType NotificationType { get; set; }

        public InteractiveType InteractiveType { get; set; }

        public object Content { get; set; }

        public TimeSpan Duration { get; set; }

        public NotificationResult Result { get; private set; }

        /// <summary>
        /// Wait for this job complete
        /// </summary>
        /// <returns></returns>
        public NotificationResult Wait()
        {
            return this.End(this.Begin(null, null));
        }

        public IAsyncResult Begin(AsyncCallback callback, object state)
        {
            if (Interlocked.CompareExchange(ref this._state, 1, 0) == 0)
            {
                // create wait handler
                this._waitHandler = new ManualResetEvent(false);
                // create async object
                this._asyncObject = new AsyncProcessObject()
                {
                    AsyncState = state,
                    CompletedSynchronously = false,
                    Callback = callback,
                    IsCompleted = false,
                    AsyncWaitHandle = this._waitHandler
                };

                return this._asyncObject;
            }
            else
            {
                throw new InvalidOperationException("Job has already completed");
            }
        }

        public NotificationResult End(IAsyncResult asyncResult)
        {
            if (asyncResult == null)
                throw new ArgumentNullException("asyncResult");
            if (!object.ReferenceEquals(asyncResult, this._asyncObject))
                throw new ArgumentException("Invalid asyncResult");
            // Here, we got a multi-thread problem
            // This method only guarantees weak thread-safe not strict thread-safe for better performance
            if (this._state == 1)
            {
                asyncResult.AsyncWaitHandle.WaitOne();

                this._asyncObject.DoCallbackAsync();

                return this.Result;
            }
            else if (this._state == 2)
            {
                return this.Result;
            }
            else
            {
                throw new InvalidOperationException("Job hasn't begin");
            }
        }

        public void Finish(NotificationResult result)
        {
            if (result == null)
                throw new ArgumentNullException("result");

            if (Interlocked.CompareExchange(ref this._state, 2, 1) == 1)
            {
                this.Result = result;
                this._waitHandler.Set();
            }
            else
            {
                throw new InvalidOperationException("Job has already completed or hasn't begin");
            }
        }

        protected override void DisposeOnce()
        {
            if (this._waitHandler != null)
            {
                this._waitHandler.Dispose();
                this._waitHandler = null;
            }
        }

        #region private

        private int _state = 0; // 0 for not begin, 1 for waiting, 2 for completed

        private AsyncProcessObject _asyncObject;

        private ManualResetEvent _waitHandler;

        private class AsyncProcessObject : IAsyncResult
        {
            public object AsyncState { get; set; }

            public WaitHandle AsyncWaitHandle { get; set; }

            public bool CompletedSynchronously { get; set; }

            public bool IsCompleted { get; set; }

            public AsyncCallback Callback { get; set; }

            public void DoCallbackAsync()
            {
                if (Interlocked.CompareExchange(ref this._isCalledback, 1, 0) == 0)
                {
                    BackgroundJob.Run(new Action(() =>
                        {
                            // do callback
                            if (this.Callback != null)
                                this.Callback(this);
                        }), "Notification Job Callback");
                }
            }

            private int _isCalledback = 0;
        }

        #endregion private
    }
}